/*
 * syntax.c  -- syntax parser for bsc
 * (c) Thomas Pornin 1998
 */

#include "syntax.h"
#include "lexer.h"
#include "hash.h"
#include "mem.h"
#include "parse.h"
#include "lut.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

/*
 * This function compares two symbols; the symbols are considered equal
 * if they have the same name
 */
static int cmp_symbol(void *s1, void *s2)
{
	struct symbol *t1 = s1, *t2 = s2;

	return (strcmp(t1->name, t2->name) == 0);
}

/*
 * This function hashes a symbol: it returns a hashed version of the name
 */
static u32 hash_symbol(void *s)
{
	struct symbol *t = s;

	return hash_string(t->name);
}

/*
 * This function creates a new symbol table
 */
struct HT *new_symbol_table(void)
{
	return newHT(211, cmp_symbol, hash_symbol);
}

/*
 * This function adds a symbol to the symbol table
 */
int add_symbol(struct symbol *s, struct HT *symtab)
{
	if (putHT(symtab, s)) {
		fprintf(stderr, "bsc: duplicate identifier '%s'\n", s->name);
		exit(1);
	}
	return 0;
}

/*
 * This function retreives a symbol with the given name from the
 * symbol table
 */
struct symbol *get_symbol(char *name, struct HT *symtab)
{
	struct symbol t;

	t.name = getmem(1 + strlen(name));
	strcpy(t.name, name);
	return getHT(symtab, &t);
}

/*
 * This function creates a new symbol in the symbol table, with a name
 * different from all names in the table
 */
struct symbol *new_symbol(struct HT *symtab)
{
	static unsigned int u = 0;
	char *n;
	struct symbol *s;

	s = getmem(sizeof(struct symbol));
	n = getmem(14);
	do {
		sprintf(n, "__newsym_%04x", u ++);
		s->name = n;
	} while (get_symbol(n, symtab));
	add_symbol(s, symtab);
	return s;
}

struct HT *symtab;

static struct token *token_list;
static int tklp;
static int parse_finished;
static long cur_in_line;
static struct action *main_action_list;
static long malp;
static long gcr = 2;

struct action *add_action(struct action *list, int l, struct action n)
{
	struct action *x = list;

	if (l != 0 && (l % MEM_GRAN) == 0) x = incmem(list, l * sizeof(struct action), (l + MEM_GRAN) * sizeof(struct action));
	x[l] = n;
	return x;
}

void aa(struct action n)
{
	main_action_list = add_action(main_action_list, malp ++, n);
}

/*
 * On parse error, barf unkindly
 */
void yyerror(void)
{
	fprintf(stderr, "bsc: parse error on line %ld\n", cur_in_line);
	exit(1);
}

/*
 * This function initialiazes the parser; input is f
 */
static void init_parser(FILE *f)
{
	init_lexer();

	token_list = lex(f);
	tklp = 0;
	parse_finished = 0;
	symtab = new_symbol_table();
	main_action_list = getmem(MEM_GRAN * sizeof(struct action));
	malp = 0;
}

/*
 * The yylex() function is called from the parser generated by yacc
 * It returns the next relevant token, 0 if the end of the input has been
 * reached.
 *
 * Warning: this is NOT reentrant
 */
int yylex(void)
{
	int i;
	struct token *tkl = token_list;

	for (; tkl[tklp].type != NONE; tklp ++) {
		cur_in_line = tkl[tklp].line;
		i = tklp ++;
		switch (tkl[i].type) {
			case SEMIC:
				return 0;
			case NUMBER:
				yylval.num = strtol(tkl[i].name, 0, 0);
				return NUMBER;
			case NAME:
				yylval.id = tkl[i].name;
				if (!strcmp(tkl[i].name, "ext")) return EXT;
				if (!strcmp(tkl[i].name, "tab")) return TAB;
				if (!strcmp(tkl[i].name, "bit")) return BIT;
				if (!strcmp(tkl[i].name, "loop")) return LOOP;
				if (!strcmp(tkl[i].name, "void")) return VOID;
				return NAME;
			default:
				yylval.id = tkl[i].name;
				return tkl[i].type;
		}
	}
	parse_finished = 1;
	return 0;
}

void new_regs(long *r, int n)
{
	int i;

	for (i = 0; i < n; i ++) r[i] = gcr ++;
}

/*
 * The add_action_*() functions are invoked from the parser,
 * corresponding to the meaning of the parsed input
 */
void add_action_deftab(struct symbol *s, struct num_list nl)
{
#ifdef DEBUG
	fprintf(stderr, "aa_deftab: %s:%d <- %d\n", s->name, s->width[0], s->width[1]);
#endif
	def_lut(s, nl);
}

void add_action_defext(struct symbol *s, struct num_list nl)
{
#ifdef DEBUG
	fprintf(stderr, "aa_defext: %s:%d <- %d\n", s->name, s->width[0], s->width[1]);
#endif
	def_ext(s, nl);
}

void add_action_defbit(struct symbol *s)
{
	int i;
	struct action a;

#ifdef DEBUG
	fprintf(stderr, "aa_defbit: %s:%d\n", s->name, s->width[0]);
#endif
	s->u.reg = getmem(s->width[0] * sizeof(long));
	new_regs(s->u.reg, s->width[0]);
	for (i = 0; i < s->width[0]; i ++) {
		a.type = B_CREAT;
		a.r1 = s->u.reg[i];
		aa(a);
	}
}

void add_action_fun(struct symbol *f, struct symbol *s, struct symbol *t)
{
	int j;
	struct action a;
	struct action *l, *m;

#ifdef DEBUG
	fprintf(stderr, "aa_fun: %s\n", s->name);
#endif
	for (l = f->u.def, j = 0; l->type == B_ARG; l ++) {
		a.type = B_CREAT; a.r1 = l->r1; aa(a);
		a.type = B_COPY; a.r1 = s->u.reg[j ++]; a.r2 = l->r1; aa(a);
	}
	m = l;
	for (; l->type == B_RET; l ++) {
		a.type = B_CREAT; a.r1 = l->r1; aa(a);
	}
	for (; l->type != B_ENDF; l ++) aa(*l);
	for (j = 0, l = m; l->type == B_RET; l ++) {
		a.type = B_COPY; a.r1 = l->r1; a.r2 = t->u.reg[j ++]; aa(a);
		a.type = B_ZAP; a.r1 = l->r1; aa(a);
	}
	for (l = f->u.def; l->type == B_ARG; l ++) {
		a.type = B_ZAP; a.r1 = l->r1; aa(a);
	}
}

void add_action_copy(struct symbol *s, struct symbol *t)
{
	struct action a;
	int i;

#ifdef DEBUG
	fprintf(stderr, "aa_copy: %s:%d -> %s:%d\n", s->name, s->width[0], t->name, t->width[0]);
#endif
	if (s->width[0] != t->width[0]) {
		fprintf(stderr, "bsc: size mismatch\n");
		exit(1);
	}
	for (i = 0; i < t->width[0]; i ++) {
		a.type = B_COPY;
		a.r1 = s->u.reg[i];
		a.r2 = t->u.reg[i];
		aa(a);
	}
}

void add_action_not(struct symbol *s, struct symbol *t)
{
	struct action a;
	int i;

#ifdef DEBUG
	fprintf(stderr, "aa_not: %s:%d -> %s:%d\n", s->name, s->width[0], t->name, t->width[0]);
#endif
	if (s->width[0] != t->width[0]) {
		fprintf(stderr, "bsc: size mismatch\n");
		exit(1);
	}
	for (i = 0; i < t->width[0]; i ++) {
		a.type = B_NOT;
		a.r1 = s->u.reg[i];
		a.r2 = t->u.reg[i];
		aa(a);
	}
}

void add_action_xor(struct symbol *s, struct symbol *t, struct symbol *u)
{
	struct action a;
	int i;

#ifdef DEBUG
	fprintf(stderr, "aa_xor: %s:%d, %s:%d -> %s:%d\n", s->name, s->width[0], t->name, t->width[0], u->name, u->width[0]);
#endif
	if (s->width[0] != t->width[0] || t->width[0] != u->width[0]) {
		fprintf(stderr, "bsc: size mismatch\n");
		exit(1);
	}
	for (i = 0; i < t->width[0]; i ++) {
		a.type = B_XOR;
		a.r1 = s->u.reg[i];
		a.r2 = t->u.reg[i];
		a.r3 = u->u.reg[i];
		aa(a);
	}
}

void add_action_or(struct symbol *s, struct symbol *t, struct symbol *u)
{
	struct action a;
	int i;

#ifdef DEBUG
	fprintf(stderr, "aa_or: %s:%d, %s:%d -> %s:%d\n", s->name, s->width[0], t->name, t->width[0], u->name, u->width[0]);
#endif
	if (s->width[0] != t->width[0] || t->width[0] != u->width[0]) {
		fprintf(stderr, "bsc: size mismatch\n");
		exit(1);
	}
	for (i = 0; i < t->width[0]; i ++) {
		a.type = B_OR;
		a.r1 = s->u.reg[i];
		a.r2 = t->u.reg[i];
		a.r3 = u->u.reg[i];
		aa(a);
	}
}

void add_action_and(struct symbol *s, struct symbol *t, struct symbol *u)
{
	struct action a;
	int i;

#ifdef DEBUG
	fprintf(stderr, "aa_and: %s:%d, %s:%d -> %s:%d\n", s->name, s->width[0], t->name, t->width[0], u->name, u->width[0]);
#endif
	if (s->width[0] != t->width[0] || t->width[0] != u->width[0]) {
		fprintf(stderr, "bsc: size mismatch\n");
		exit(1);
	}
	for (i = 0; i < t->width[0]; i ++) {
		a.type = B_AND;
		a.r1 = s->u.reg[i];
		a.r2 = t->u.reg[i];
		a.r3 = u->u.reg[i];
		aa(a);
	}
}

void add_action_concat(struct symbol *s, struct symbol *t, struct symbol *u)
{
	int i;

#ifdef DEBUG
	fprintf(stderr, "aa_concat: %s:%d, %s:%d -> %s:%d\n", s->name, s->width[0], t->name, t->width[0], u->name, u->width[0]);
#endif
	if (s->width[0] + t->width[0] != u->width[0]) {
		fprintf(stderr, "bsc: size mismatch\n");
		exit(1);
	}
	u->u.reg = getmem(u->width[0] * sizeof(long));
	for (i = 0; i < s->width[0]; i ++) u->u.reg[i] = s->u.reg[i];
	for (i = 0; i < t->width[0]; i ++) u->u.reg[i + s->width[0]] = t->u.reg[i];
}

struct action *parse(FILE *f, struct symbol *inout[2])
{
	struct action a;

	init_parser(f);
	while (!parse_finished) yyparse();
	a.type = B_END;
	aa(a);
	inout[0] = get_symbol("input", symtab);
	if (inout[0] == 0) {
		fprintf(stderr, "bsc: warning: no input specified\n");
	}
	inout[1] = get_symbol("output", symtab);
	if (inout[1] == 0) {
		fprintf(stderr, "bsc: warning: no output specified\n");
	}
	return main_action_list;
}
